#include "../Defines.h"
#include "Parsing.h"
#include "../SQLInterface/Tuple.h"
#include "../SQLInterface/Database.h"
#include "../Tools/Sort.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

static const char* const HTMLTableTableBegin = "</th></tr><tr><td class=\"yfnc_tabledata1\" nowrap align=\"right\">";
static const char* const HTMLTableTupleDelimiter = "/td></tr><tr><td class=\"yfnc_tabledata1\" nowrap align=\"right\">";
static const char* const HTMLTableColumnDelimiter = "/td><td class=\"yfnc_tabledata1\" align=\"right\">";

long int search_file_for_string(FILE* file, const char* const string);

result_type parse_symbols(char*** symbols, int* length)
{
    char buf[100];
    int i, i2, i3;
    fpos_t fileStart;
    FILE* fp = fopen("NYSEsymbols.txt", "r");

    if(fp == NULL)
    {
        perror("Error opening NYSEsymbols.txt");
        return FATAL_ERROR;
    }
    fgetpos(fp, &fileStart);

    // get number of symbols...
    for(i = 0; fgets(buf, 100, fp) != NULL; i++);

    *length = i;

    // allocate memory for # of symbols
    *symbols = (char**)malloc(sizeof(char*) * (*length));
    if(*symbols == NULL)
    {
        perror("Error allocating memory for symbol list");
        return FATAL_ERROR;
    }

    // allocate memory for each symbol
    for(i2 = 0; i2 < *length; i2++)
    {
        (*symbols)[i2] = (char*)malloc(sizeof(char) * 6);
        if((*symbols)[i2] == NULL)
        {
            perror("Error allocating memory for symbol list");
            return FATAL_ERROR;
        }
    }

    // copy each symbol + NULL term
    fsetpos(fp, &fileStart);
    for(i2 = 0; i2 < *length; i2++)
    {
        if(fgets(buf, 100, fp) != NULL)
        {
            for(i3 = 0; i3 < 5; i3++)
            {
                if(buf[i3] == ' ' || buf[i3] == 0 || buf[i3] == '\n' || buf[i3] == '\t')
                {
                    break;
                }
                else if(buf[i3] == '.')// yahoo does this, so we do too
                {
                    (*symbols)[i2][i3] = '-';
                }
                else
                {
                    (*symbols)[i2][i3] = buf[i3];
                }
            }
            (*symbols)[i2][i3] = 0;
        }
        else
        {
            printf("Error attempting to read line %d of NYSEsymbols.txt", i2);
            fclose(fp);
            return GENERAL_ERROR;
        }
    }

    fclose(fp);
    return SUCCESS;
}

result_type parse_symbols_names(char*** symbols, int* length, char*** nameList)
{
    char buf[100];
    char* symbolToken;
    char* nameToken;
    int i, i2, i3;
    fpos_t fileStart;
    FILE* fp = fopen("NYSEsymbols.txt", "r");

    if(fp == NULL)
    {
        perror("Error opening NYSEsymbols.txt");
        return FATAL_ERROR;
    }
    fgetpos(fp, &fileStart);

    // get number of symbols...
    for(i = 0; fgets(buf, 100, fp) != NULL; i++);

    *length = i;

    // allocate memory for # of symbols
    *symbols = (char**)malloc(sizeof(char*) * (*length));
    *nameList = (char**)malloc(sizeof(char*) * (*length));
    if((*symbols == NULL) || (*nameList == NULL))
    {
        perror("Error allocating memory for symbol list");
        return FATAL_ERROR;
    }

    // allocate memory for each symbol
    for(i2 = 0; i2 < *length; i2++)
    {
        (*symbols)[i2] = (char*)malloc(sizeof(char) * 6);
        if((*symbols)[i2] == NULL)
        {
            perror("Error allocating memory for symbol list");
            return FATAL_ERROR;
        }
    }

    // copy each symbol + NULL term
    fsetpos(fp, &fileStart);
    for(i2 = 0; i2 < *length; i2++)
    {
        if(fgets(buf, 1000, fp) != NULL)
        {
            // get symbol
            symbolToken = strtok(buf,"\t");
            for(i3 = 0; i3 < 5; i3++)
            {
                if(symbolToken[i3] == ' ' || symbolToken[i3] == 0 || symbolToken[i3] == '\n' || symbolToken[i3] == '\t')
                {
                    break;
                }
                else if(buf[i3] == '.')// yahoo does this, so we do too
                {
                    (*symbols)[i2][i3] = '-';
                }
                else
                {
                    (*symbols)[i2][i3] = symbolToken[i3];
                }
            }
            (*symbols)[i2][i3] = 0;

            // then get name
            nameToken = strtok(NULL,"\n");
            (*nameList)[i2] = (char*)malloc((strlen(nameToken) + 1) * sizeof(char));
            strcpy((*nameList)[i2], nameToken);

        }
        else
        {
            printf("Error attempting to read line %d of NYSEsymbols.txt", i2);
            fclose(fp);
            return GENERAL_ERROR;
        }
    }

    fclose(fp);
    return SUCCESS;
}

// TODO: this could conceivably be threaded
 
/*
 * This function parses the table 'yfnc_tabledata1' in an html 
 * file specified with the input parameter fileName. It creates
 * a tuple_t for every row in the table and inserts it as an 
 * element in the linked list tupleList.
 *
 *  Output tuples contain the following values
 *  stock label (from input parameter)
 *  Date
 *  Open
 *  High
 *  Low
 *  Close
 *  Volume
 *  Adj Close
 *
 * probably only need date and adj close...can fix this later
 *
 * NOTE: Memory is allocated for nodes, tuples, and strings in
 *       the tuples created by this function. The caller is 
 *       responsible for managing the returned memory.
 *
 * defines: TUPLES_PER_HTML_PAGE = number of tuples this function will parse
 *          HTML_TABLE_NUM_PARAMS = number of attributes parsed in html table
 *
 * params:  filename  - name of the file to be parsed
 *          tupleList - list new values are inserted into 
 *          @return   - 
 */
result_type parse_html_table(const char* const fileName, node_t* tupleList, const char* const symbol)
{
    FILE* fp = fopen(fileName, "r");
    long int offset = 0;
    char buffer[100]; //#define?
    int i, i2, i3, i4, tupleSuccess;
    tuple_t* tuple;

    if(fp == NULL)
    {
        // TODO: convert to perror
        printf("Error opening %s\n", fileName);
        return FATAL_ERROR;
    }
    offset = search_file_for_string(fp, HTMLTableTableBegin);

    if(offset < 0)
    {
        fclose(fp);
        return END_OF_DATA;
    }

    fseek(fp, offset, SEEK_SET);

    // this is hacky...and more cases need to be considered.
    // TODO: figure out a better fix than this + 10...
    for(i = 0; i < (TUPLES_PER_HTML_PAGE + 10); i++) // +10 catches some errors...
    {
        // allocate tuple memory and initialize
        tuple = initTuple(TABLE_STOCK_DATA);

        // init stock label..
        tuple->params[0] = (char*)malloc((sizeof(char) * strlen(symbol)) + 1);
        strcpy((char*)tuple->params[0], symbol);

        //TODO: handle errors from previous mem functions

        i2 = 1;
        do
        {
            // get param
            for(i3 = 0; buffer[i3-1] != '<'; i3++)
            {
                buffer[i3] = (char)fgetc(fp);
            }
            buffer[i3-1] = 0; // insert null term

            // insert into tuple struct

            // handle special cases...
            if(i2 == STOCK_DATA_PARAM_DATE)
            {
                // convert date to proper format if necessary...
                if(!isdigit(buffer[0]))
                {
                    tuple->params[i2] = webDateToSqlDate(buffer);
                }
                else
                {
                    tuple->params[i2] = (char*)malloc((sizeof(char) * strlen(buffer)) + 1);
                    strcpy((char*)(tuple->params[i2]), buffer);
                }
            }
            else if(i2 == STOCK_DATA_PARAM_VOLUME)
            {
                // remove commas from volume
                for(i3 = 0; buffer[i3] != 0; i3++)
                {
                    if(buffer[i3] == ',')
                    {
                        for(i4 = i3; buffer[i4] != 0; i4++)
                        {
                            buffer[i4] = buffer[i4 + 1];
                        }
                    }
                }
                tuple->params[i2] = (char*)malloc((sizeof(char) * strlen(buffer)) + 1);
                strcpy((char*)(tuple->params[i2]), buffer);
            }
            else
            {
                tuple->params[i2] = (char*)malloc((sizeof(char) * strlen(buffer)) + 1);
                strcpy((char*)(tuple->params[i2]), buffer);
            }

            // continue to next parameter unless we're at the last param
            if(++i2 < STOCK_DATA_PARAMS)
            {
                for(i3 = 0; i3 < strlen(HTMLTableColumnDelimiter); i3++)
                {
                    if(HTMLTableColumnDelimiter[i3] != (char)fgetc(fp))
                    {
                        //advance and fill buffer anyway...
                        for(i3 = 0; (char)fgetc(fp) != '>' && i3 < 100; i3++){}

                        for(i3 = 0; (buffer[i3-1] != '<') && i3 < 100; i3++)
                        {
                            buffer[i3] = (char)fgetc(fp);
                        }
                        buffer[i3-1] = 0;

                        //check for known errors here and handle...
                        if(strstr(buffer,"Dividend") != NULL
                           || strstr(buffer,"dividend") != NULL
                           || strstr(buffer, "Stock Split") != NULL)
                        {
                            //free memory and continue processing next tuple
                            for(i3 = 0; i3 < i2; i3++)
                            {
                                free(tuple->params[i3]);
                            }
                            free(tuple->params);
                            free(tuple);

                            //to exit tuple loop...
                            i2 = STOCK_DATA_PARAMS;
                            tupleSuccess = 0;
                            break;
                        }
                        else
                        {
                            fclose(fp);
                            return GENERAL_ERROR;
                        }
                    }
                }// end pass delimiter loop
            }
            else
            {
                tupleSuccess = 1;
                break;
            }

        }while(i2 < STOCK_DATA_PARAMS);// end param loop

        if(tupleSuccess)
        {
            insert_node(tupleList, tuple);
        }

        //advance to next column...
        for(i3 = 0; i3 < strlen(HTMLTableTupleDelimiter); i3++)
        {
            if(HTMLTableTupleDelimiter[i3] != (char)fgetc(fp))
            {
                //advance and fill buffer anyway...
                for(i3 = 0; (char)fgetc(fp) != '>' && i3 < 100; i3++){}

                for(i3 = 0; (buffer[i3-1] != '<') && i3 < 100; i3++)
                {
                    buffer[i3] = (char)fgetc(fp);
                }
                buffer[i3-1] = 0;

                // assume end of data for now...
                if(strstr(buffer,"* "))
                {
                    fclose(fp);
                    return END_OF_DATA;
                }
                else 
                {
                    fclose(fp);
                    return GENERAL_ERROR;
                }
            }

        }

    }// end tuple loop

    fclose(fp);
    return SUCCESS;
}

// This is a horrible search...should probably replace later
long int search_file_for_string(FILE* file, const char* const string)
{
    char buffer[BUFFER_SIZE];
    int bufferIndex= 0; 
    int strIndex = 0;
    int count = 0;
     
    do
    {
        count = fread(buffer, 1, BUFFER_SIZE, file);

        //have to search manually since the string might span buffers...
        for(bufferIndex = 0; bufferIndex < count; bufferIndex++)
        {
            if(buffer[bufferIndex] == string[strIndex])
            {
                strIndex++;
                if(strIndex == strlen(string))
                {
                    return ((ftell(file) - BUFFER_SIZE) + bufferIndex + 1);//should put fp at start of first date
                }
            }
            else
            {
                strIndex = 0;
            }
        }

    }
    while(count == BUFFER_SIZE);

    return -1;
}
